#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/time.h>
#include <dirent.h>
#include <sys/sysmacros.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <poll.h>
#include <ctype.h>
#include <sys/xattr.h>

#include <openssl/evp.h>
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/err.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <locale.h>
#include <libintl.h>

#define GMSEAL_LOCK "/var/run/audisp-gmseal.lock"
#include "../../lib/gm_crypto.h"

#define SEAL_PATH "/var/log/audit/audit.log.seal"
#define IDX_PATH "/var/log/audit/audit.log.idx"
#define LOG_PATH "/var/log/audit/audit.log"
#define SM2_ID "1234567812345678"


#define P12_PATH "/etc/pki/audit/sm2.p12"
#define SALT_PATH "/etc/pki/audit/p12_salt"
#define PASS_PATH "/etc/pki/audit/p12_pass"

#ifndef GETTEXT_PACKAGE
#define GETTEXT_PACKAGE "audit-gm"
#endif
#ifndef LOCALEDIR
#define LOCALEDIR "/usr/local/share/locale"
#endif
#ifndef _
#define _(s) gettext(s)
#endif
#ifndef N_
#define N_(s) (s)
#endif
static void i18n_init(void)
{ 
    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); 
    bind_textdomain_codeset(GETTEXT_PACKAGE,"UTF-8"); 
    textdomain(GETTEXT_PACKAGE); 
}



// 模拟 seal 文件解析结构
struct seal {
    char *version;
    char *hash;
    char *sign;
    char *sm2_id;
    char *path;
    char *records;
    char *chain;
    char *key_fpr;
    char *index_sm3;
    char *cert_b64;
    char *sig_b64;
};


// 若 idx 不存在，则用 audit.log 的属主/权限/SELinux 标签创建一个“空壳”文件
static int ensure_idx_placeholder(void) {
    struct stat sl, si;
    if (stat(IDX_PATH, &si) == 0) return 0;          // 已存在就不动
    if (stat(LOG_PATH, &sl) != 0) return -1;

    mode_t mode = sl.st_mode & 0777;
    int fd = open(IDX_PATH, O_WRONLY | O_CREAT | O_TRUNC, mode);
    if (fd < 0) return -1;

    // 继承属主/属组（失败不致命）
    (void)fchown(fd, sl.st_uid, sl.st_gid);

    // 继承 SELinux 标签（失败不致命/无该特性则忽略）
    char sbuf[256];
    ssize_t n = getxattr(LOG_PATH, "security.selinux", sbuf, sizeof(sbuf));
    if (n > 0) (void)fsetxattr(fd, "security.selinux", sbuf, n, 0);

    fsync(fd);
    close(fd);

    // 刷目录
    char dir[PATH_MAX]; strncpy(dir, IDX_PATH, sizeof(dir)-1); dir[sizeof(dir)-1]=0;
    char *slash = strrchr(dir, '/'); if (slash) { if (slash==dir) slash[1]=0; else *slash=0; }
    DIR *d = opendir(dir); if (d) { int dfd=dirfd(d); if (dfd>=0) fsync(dfd); closedir(d); }
    return 0;
}

static int read_idx_tail(unsigned long long *out_i, char chash_hex[65]) {
    FILE *ix = fopen(IDX_PATH, "r");
    if (!ix) return -1;
    char buf[1024], lhex[128], chex[128];
    unsigned long long i=0, off=0, last_i=0; char last_ch[65]={0};
    while (fgets(buf,sizeof(buf),ix)) {
        if (sscanf(buf,"i=%llu off=%llu lhash=%127s chash=%127s",&i,&off,lhex,chex)==4) {
            last_i = i;
            strncpy(last_ch, chex, 64); last_ch[64]=0;
        }
    }
    fclose(ix);
    if (out_i) *out_i = last_i;
    if (chash_hex && last_i>0) strncpy(chash_hex, last_ch, 65);
    return 0;
}

static int read_file_first_line_trim(const char *path, char **out) {
    FILE *f=fopen(path,"r"); if(!f) return -1;
    size_t cap=0; ssize_t n=getline(out,&cap,f); fclose(f);
    if(n<=0) { free(*out); *out=NULL; return -1; }
    while(n && ((*out)[n-1]=='\n'||(*out)[n-1]=='\r')) (*out)[--n]=0;
    return 0;
}

static void hexdump(const unsigned char *buf, size_t len, char *out, size_t outlen)
{
    static const char hex[] = "0123456789abcdef";
    if (outlen < len * 2 + 1) return;
    for (size_t i = 0; i < len; i++) {
        out[i * 2] = hex[(buf[i] >> 4) & 0xF];
        out[i * 2 + 1] = hex[buf[i] & 0xF];
    }
    out[len * 2] = 0;
}

/* 计算任意文件的 SM3(hex)；成功返回0，失败返回-1 */
static int sm3_file_hex(const char *path, char out_hex[65]) {
    FILE *f = fopen(path, "rb");
    if (!f) return -1;
    const EVP_MD *sm3 = EVP_get_digestbyname("sm3");
    if (!sm3) { fclose(f); return -1; }
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    if (!ctx) { fclose(f); return -1; }
    if (EVP_DigestInit_ex(ctx, sm3, NULL) != 1) { EVP_MD_CTX_free(ctx); fclose(f); return -1; }

    unsigned char buf[16384];
    size_t r;
    while ((r = fread(buf,1,sizeof(buf),f)) > 0) {
        if (EVP_DigestUpdate(ctx, buf, r) != 1) { EVP_MD_CTX_free(ctx); fclose(f); return -1; }
    }
    fclose(f);

    unsigned char dgst[32]; unsigned int dl=0;
    if (EVP_DigestFinal_ex(ctx, dgst, &dl) != 1 || dl != 32) { EVP_MD_CTX_free(ctx); return -1; }
    EVP_MD_CTX_free(ctx);

    static const char hex[]="0123456789abcdef";
    for (unsigned i=0;i<32;i++) { out_hex[i*2]=hex[(dgst[i]>>4)&0xF]; out_hex[i*2+1]=hex[dgst[i]&0xF]; }
    out_hex[64]=0;
    return 0;
}

static char *b64_encode(const unsigned char *buf, size_t len)
{
    BIO *b64 = BIO_new(BIO_f_base64());
    BIO *mem = BIO_new(BIO_s_mem());
    if (!b64 || !mem) { if (b64) BIO_free(b64); if (mem) BIO_free(mem); return NULL; }
#ifdef BIO_FLAGS_BASE64_NO_NL
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
#endif
    BIO_push(b64, mem);
    if (BIO_write(b64, buf, (int)len) <= 0) { BIO_free_all(b64); return NULL; }
    (void)BIO_flush(b64);
    BUF_MEM *bptr = NULL;
    BIO_get_mem_ptr(b64, &bptr);
    char *out = malloc(bptr->length + 1);
    if (!out) { BIO_free_all(b64); return NULL; }
    memcpy(out, bptr->data, bptr->length);
    out[bptr->length] = 0;
    BIO_free_all(b64);
    return out;
}

static int local_reseal_from_idx_tail(void) {
    // 1) 取得 idx 尾项（records/chain）
    unsigned long long rec = 0; char chain_hex[65]={0};
    if (read_idx_tail(&rec, chain_hex) != 0) return -1;
    if (rec == 0) memset(chain_hex, '0', 64), chain_hex[64]=0;

    // 2) 计算 index_sm3
    char idx_hex[65]={0};
    if (sm3_file_hex(IDX_PATH, idx_hex) != 0) return -1;

    // 3) 读取口令/盐，解析 P12
    char *pass_in=NULL;
    if (read_file_first_line_trim(PASS_PATH, &pass_in) != 0) return -1;

    unsigned char salt[16]={0};
    {
        FILE *sf=fopen(SALT_PATH,"r"); if(!sf){ free(pass_in); return -1; }
        char hex[33]={0}; if(!fgets(hex,sizeof(hex),sf)){ fclose(sf); free(pass_in); return -1; }
        fclose(sf);
        for (int i=0;i<16;i++){ unsigned hi,lo; if (sscanf(&hex[i*2],"%1x%1x",&hi,&lo)!=2){ free(pass_in); return -1; } salt[i]=(hi<<4)|lo; }
    }

    // 派生/直用 pass（与插件一致）
    char passhex[65]={0}; int is_hex64 = (strlen(pass_in)==64);
    if (is_hex64) {
        for(int i=0;i<64;i++){ char c=pass_in[i]; if(!((c>='0'&&c<='9')||(c>='a'&&c<='f'))){ is_hex64=0; break; } }
    }
    if (!is_hex64) {
        const EVP_MD *sm3 = EVP_get_digestbyname("sm3"); if(!sm3){ free(pass_in); return -1; }
        unsigned char derived[32];
        if(!PKCS5_PBKDF2_HMAC(pass_in, strlen(pass_in), salt, sizeof(salt), 200000, sm3, 32, derived)){ free(pass_in); return -1; }
        static const char hx[]="0123456789abcdef";
        for (int i=0;i<32;i++){ passhex[i*2]=hx[(derived[i]>>4)&0xF]; passhex[i*2+1]=hx[derived[i]&0xF]; }
        passhex[64]=0;
    } else {
        strncpy(passhex, pass_in, 65);
    }

    EVP_PKEY *pkey=NULL; X509 *cert=NULL;
    FILE *pf=fopen(P12_PATH,"rb"); if(!pf){ OPENSSL_cleanse(pass_in,strlen(pass_in)); free(pass_in); return -1; }
    PKCS12 *p12=d2i_PKCS12_fp(pf,NULL); fclose(pf); if(!p12){ OPENSSL_cleanse(pass_in,strlen(pass_in)); free(pass_in); return -1; }
    if (!PKCS12_parse(p12, passhex[0]?passhex:pass_in, &pkey, &cert, NULL)) { PKCS12_free(p12); OPENSSL_cleanse(pass_in,strlen(pass_in)); free(pass_in); return -1; }
    PKCS12_free(p12);
    OPENSSL_cleanse(pass_in,strlen(pass_in)); free(pass_in);

    // 4) 证书指纹（SM3 over DER）
    unsigned char *der=NULL; int derlen=i2d_X509(cert,&der); if(derlen<=0){ EVP_PKEY_free(pkey); X509_free(cert); return -1; }
    unsigned char fpr_raw[32]; unsigned int fpr_len=0;
    EVP_MD_CTX *mctx=EVP_MD_CTX_new();
    if(!mctx || EVP_DigestInit_ex(mctx, EVP_get_digestbyname("sm3"), NULL)!=1 ||
       EVP_DigestUpdate(mctx, der, derlen)!=1 || EVP_DigestFinal_ex(mctx, fpr_raw, &fpr_len)!=1 || fpr_len!=32){
        EVP_MD_CTX_free(mctx); OPENSSL_free(der); EVP_PKEY_free(pkey); X509_free(cert); return -1;
    }
    EVP_MD_CTX_free(mctx); OPENSSL_free(der);
    char fpr_hex[65]={0}; hexdump(fpr_raw,32,fpr_hex,sizeof(fpr_hex));

    // 5) 组装 canon（字段/顺序必须与插件一致）
    char *canon=NULL;
    if (asprintf(&canon,
        "version=1\nhash=sm3\nsign=sm2\nsm2_id=%s\npath=%s\nrecords=%llu\nchain=%s\nkey_fpr=%s\nindex_sm3=%s\n",
        SM2_ID, LOG_PATH, (unsigned long long)rec, chain_hex, fpr_hex, idx_hex) < 0){
        EVP_PKEY_free(pkey); X509_free(cert); return -1;
    }

    // 6) SM2 签名（用你现有 gm_crypto）
    unsigned char *sig=NULL; size_t siglen=0;
    if (gm_sm2_sign(pkey, (const unsigned char*)canon, strlen(canon), SM2_ID, &sig, &siglen) != 0) {
        free(canon); EVP_PKEY_free(pkey); X509_free(cert); return -1;
    }
    char *sigb64 = b64_encode(sig, siglen); OPENSSL_free(sig);

    unsigned char *der2=NULL; int der2len=i2d_X509(cert,&der2);
    char *certb64=NULL; if (der2len>0) { certb64=b64_encode(der2,der2len); OPENSSL_free(der2); }
    EVP_PKEY_free(pkey); X509_free(cert);
    if (!sigb64 || !certb64){ free(canon); free(sigb64); free(certb64); return -1; }

    // 7) === 就地重写 seal：不 rename，不改权限/标签 ===
    {
        int fd = open(SEAL_PATH, O_WRONLY | O_TRUNC);
        struct stat sl;
        if (fd < 0) {
            // 不存在则按 audit.log 继承属主与权限创建
            if (stat(LOG_PATH, &sl) == 0)
                fd = open(SEAL_PATH, O_WRONLY | O_CREAT | O_TRUNC, sl.st_mode & 0777);
            else
                fd = open(SEAL_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0640);
            if (fd < 0) { free(canon); free(sigb64); free(certb64); return -1; }
            if (stat(LOG_PATH, &sl) == 0) (void)fchown(fd, sl.st_uid, sl.st_gid);
        }

        FILE *sf = fdopen(dup(fd), "w");
        if (!sf) { int er=errno; close(fd); free(canon); free(sigb64); free(certb64); errno=er; return -1; }
        fprintf(sf, "# audit-seal v1 (SM2/SM3)\n%s", canon);
        fprintf(sf, "cert_der_base64=%s\nsig_base64=%s\n", certb64, sigb64);
        fflush(sf); fsync(fileno(sf)); fclose(sf);
        fsync(fd); close(fd);

        // 刷目录
        char dir[PATH_MAX]; strncpy(dir, SEAL_PATH, sizeof(dir)-1); dir[sizeof(dir)-1]=0;
        char *slash = strrchr(dir, '/'); if (slash) { if (slash==dir) slash[1]=0; else *slash=0; }
        DIR *d = opendir(dir); if (d) { int dfd=dirfd(d); if (dfd>=0) fsync(dfd); closedir(d); }
    }
    free(canon); free(sigb64); free(certb64);
    return 0;
}

// REPLACE the whole function in audit-recover.c
// Return values:
//   0  -> signal sent (or treated as success)
//  -1  -> transient failure (e.g., EAGAIN); caller may retry
//  -2  -> unreachable or not permitted (EPERM/ESRCH/ENOENT); caller should NOT retry
static int poke_gmseal_recheck(void)
{
    FILE *f = fopen(GMSEAL_LOCK, "r");
    if (!f) return -2;  // lock 不存在，视为不可达
    long pid = 0;
    int ok = (fscanf(f, "%ld", &pid) == 1 && pid > 1);
    fclose(f);
    if (!ok) return -2;

    // 先探测进程是否存在；EPERM 也表示“存在但无权限”
    if (kill((pid_t)pid, 0) != 0) {
        if (errno == EPERM || errno == ESRCH) return -2;
        return -1;
    }

    // 尝试发送 SIGUSR1；EPERM 视作“不可达”，不要打印
    if (kill((pid_t)pid, SIGUSR1) != 0) {
        if (errno == EPERM || errno == ESRCH) return -2;
        // 其他错误不打印，交给上层决定是否重试
        return -1;
    }
    return 0;
}



static void trim_inplace(char *s) {
    if (!s) return;
    size_t n = strlen(s);
    while (n && (s[n-1]=='\n' || s[n-1]=='\r' || isspace((unsigned char)s[n-1]))) n--;
    s[n] = 0;
    size_t i = 0;
    while (s[i] && isspace((unsigned char)s[i])) i++;
    if (i) memmove(s, s+i, strlen(s+i)+1);
}

static int safe_streq(const char *a, const char *b) {
    return a && b && strcmp(a,b)==0;
}




// 检查文件是否存在
int file_exists(const char *path) {
    struct stat st;
    return (stat(path, &st) == 0);
}



// === NEW: 统计文本行数（快速版）===
static unsigned long long count_lines_fast(const char *path) {
    int fd = open(path, O_RDONLY);
    if (fd < 0) return 0;
    char buf[1<<16];
    unsigned long long c = 0;
    for (;;) {
        ssize_t n = read(fd, buf, sizeof(buf));
        if (n == 0) break;
        if (n < 0) { if (errno==EINTR) continue; close(fd); return c; }
        for (ssize_t i=0;i<n;i++) if (buf[i]=='\n') c++;
    }
    close(fd);
    return c;
}

// === NEW: 等待文件“稳定”（size/mtime 连续 probes 次不变）===
static int wait_path_stable(const char *path, int probes, int gap_ms) {
    struct stat prev, cur;
    if (stat(path, &prev) != 0) return -1;
    int streak = 0;
    for (;;) {
        usleep(gap_ms * 1000);
        if (stat(path, &cur) != 0) return -1;
        if (cur.st_size==prev.st_size && cur.st_mtime==prev.st_mtime) {
            if (++streak >= probes) return 0;
        } else {
            streak = 0;
            prev = cur;
        }
    }
}

int parse_seal_file(const char *path, struct seal *S) {
    if (!S) return -1;
    memset(S, 0, sizeof(*S));

    FILE *f = fopen(path, "r");
    if (!f) return -1;

    char line[2048];
    while (fgets(line, sizeof(line), f)) {
        trim_inplace(line);
        if (!line[0] || line[0]=='#') continue;

        char *eq = strchr(line, '=');
        if (!eq) continue;
        *eq = 0;
        char *k = line;
        char *v = eq + 1;
        trim_inplace(k);
        trim_inplace(v);

        if (!strcmp(k,"version")) S->version = strdup(v);
        else if (!strcmp(k,"hash")) S->hash = strdup(v);
        else if (!strcmp(k,"sign")) S->sign = strdup(v);
        else if (!strcmp(k,"sm2_id")) S->sm2_id = strdup(v);
        else if (!strcmp(k,"path")) S->path = strdup(v);
        else if (!strcmp(k,"records")) S->records = strdup(v);
        else if (!strcmp(k,"chain")) S->chain = strdup(v);
        else if (!strcmp(k,"key_fpr")) S->key_fpr = strdup(v);
        else if (!strcmp(k,"index_sm3")) S->index_sm3 = strdup(v);
        else if (!strcmp(k,"cert_der_base64") || !strcmp(k,"cert_b64")) S->cert_b64 = strdup(v);
        else if (!strcmp(k,"sig_base64") || !strcmp(k,"sig_b64")) S->sig_b64 = strdup(v);
    }
    fclose(f);

    // 关键字段缺失一项就视为“不可解析”
    if (!S->version || !S->hash || !S->sign || !S->sm2_id ||
        !S->path || !S->records || !S->chain || !S->key_fpr ||
        !S->index_sm3 || !S->cert_b64 || !S->sig_b64) {
        return -1;
    }
    return 0;
}




// 获取 seal 文件的记录数
unsigned long long get_seal_records(const char *seal_path) {
    struct seal S = {0};
    if (parse_seal_file(seal_path, &S) == 0 && S.records) {
        return strtoull(S.records, NULL, 10);
    }
    return 0;
}


static int   g_log_fd  = -1;
static FILE *g_log_fp  = NULL;
static dev_t g_log_dev = 0;
static ino_t g_log_ino = 0;

static int g_idx_fd = -1;
static FILE *g_idx = NULL;
static FILE *g_log = NULL;

static void log_msg(const char *fmt, ...)
{
    if (!g_log) return;
    va_list ap;
    va_start(ap, fmt);
    time_t t = time(NULL);
    char tb[64];
    strftime(tb, sizeof(tb), "%Y-%m-%d %H:%M:%S", localtime(&t));
    fprintf(g_log, "[%s] ", tb);
    vfprintf(g_log, fmt, ap);
    fprintf(g_log, "\n");
    fflush(g_log);
    va_end(ap);
}

static void close_idx_handle(void) 
{
    if (g_idx) { fclose(g_idx); g_idx = NULL; }
    if (g_idx_fd >= 0) { close(g_idx_fd); g_idx_fd = -1; }
}

static size_t normalize_line(char *buf, size_t len)
{
    while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) len--;
    return len;
}


// REPLACE open_idx_handle：不再 fchmod 0600
static int open_idx_handle(void) {
    if (g_idx_fd >= 0) return 0;
    int fd = open(IDX_PATH, O_RDWR | O_CREAT | O_APPEND, 0); // 不改变原权限
    if (fd < 0) {
        // log_msg("open_idx_handle: open %s failed: %s", IDX_PATH, strerror(errno));
        log_msg(_("open_idx_handle: open %s failed: %s"), IDX_PATH, strerror(errno));
        return -1;
    }
    FILE *f = fdopen(dup(fd), "a+");
    // if (!f) { log_msg("open_idx_handle: fdopen failed: %s", strerror(errno)); close(fd); return -1; }
    if (!f) { log_msg(_("open_idx_handle: fdopen failed: %s"), strerror(errno)); close(fd); return -1; }
    g_idx_fd = fd; g_idx = f;
    return 0;
}

// REPLACE rebuild_idx_from_log_atomic：O_RDWR|O_CREAT(0) + ftruncate，就地重写，不改权限/标签/inode
static int rebuild_idx_from_log_atomic(const char *logpath, const char *idxpath,
                                       unsigned char out_chain[32],
                                       unsigned long long *out_lines,
                                       unsigned long long *out_offset)
{
    const EVP_MD *sm3 = EVP_get_digestbyname("sm3");
    if (!sm3) return -1;

    FILE *lf = fopen(logpath, "rb");
    if (!lf) return -1;

    // ★ 先确保有占位文件（等价 touch），让插件有机会重新附着到路径
    (void)ensure_idx_placeholder();

    int fd = open(idxpath, O_RDWR | O_CREAT, 0);     // 不传权限位，避免收紧
    if (fd < 0) { fclose(lf); return -1; }

    if (flock(fd, LOCK_EX) != 0) {
        int err = errno; close(fd); fclose(lf); errno = err; return -1;
    }

    if (ftruncate(fd, 0) != 0) {
        int err = errno; flock(fd, LOCK_UN); close(fd); fclose(lf); errno = err; return -1;
    }

    FILE *tf = fdopen(dup(fd), "w");
    if (!tf) { int err = errno; flock(fd, LOCK_UN); close(fd); fclose(lf); errno = err; return -1; }

    char *line = NULL; size_t cap = 0; ssize_t n;
    unsigned long long ln = 0, offset = 0;
    unsigned char H[32]; memset(H, 0, sizeof(H));

    while ((n = getline(&line, &cap, lf)) != -1) {
        ln++;
        size_t normlen = normalize_line(line, (size_t)n);

        EVP_MD_CTX *c1 = EVP_MD_CTX_new(); unsigned char L[32]; unsigned int lm=0;
        if (!c1 || EVP_DigestInit_ex(c1, sm3, NULL)!=1 ||
            EVP_DigestUpdate(c1, line, normlen)!=1 ||
            EVP_DigestFinal_ex(c1, L, &lm)!=1 || lm!=32) {
            if (c1) EVP_MD_CTX_free(c1);
            free(line); fclose(lf); fclose(tf);
            flock(fd, LOCK_UN); close(fd);
            return -1;
        }
        EVP_MD_CTX_free(c1);

        EVP_MD_CTX *c2 = EVP_MD_CTX_new(); unsigned char newH[32]; unsigned int hm=0;
        if (!c2 || EVP_DigestInit_ex(c2, sm3, NULL)!=1 ||
            EVP_DigestUpdate(c2, H, sizeof(H))!=1 ||
            EVP_DigestUpdate(c2, line, normlen)!=1 ||
            EVP_DigestFinal_ex(c2, newH, &hm)!=1 || hm!=32) {
            if (c2) EVP_MD_CTX_free(c2);
            free(line); fclose(lf); fclose(tf);
            flock(fd, LOCK_UN); close(fd);
            return -1;
        }
        EVP_MD_CTX_free(c2);
        memcpy(H, newH, 32);

        char lhex[65], chex[65];
        hexdump(L, 32, lhex, sizeof(lhex));
        hexdump(H, 32, chex, sizeof(chex));

        if (fprintf(tf, "i=%llu off=%llu lhash=%s chash=%s\n",
                    (unsigned long long)ln,
                    (unsigned long long)offset, lhex, chex) < 0) {
            free(line); fclose(lf); fclose(tf);
            flock(fd, LOCK_UN); close(fd);
            return -1;
        }
        offset += (unsigned long long)n;
    }

    free(line); fclose(lf);
    fflush(tf); fsync(fileno(tf)); fclose(tf);
    fsync(fd);
    flock(fd, LOCK_UN);
    close(fd);

    // 刷目录
    char dir[PATH_MAX]; strncpy(dir, idxpath, sizeof(dir)-1); dir[sizeof(dir)-1]=0;
    char *slash = strrchr(dir, '/'); if (slash) { if (slash==dir) slash[1]=0; else *slash=0; }
    DIR *d = opendir(dir); if (d) { int dfd=dirfd(d); if (dfd>=0) fsync(dfd); closedir(d); }

    if (out_chain)  memcpy(out_chain, H, 32);
    if (out_lines)  *out_lines  = ln;
    if (out_offset) *out_offset = offset;
    return 0;
}

// === NEW: 主动等到“三件套完全对齐”，必要时再重建一次 idx，并催促插件写 seal ===
// PATCH: 更强推进 seal 生成
// REPLACE the whole function in audit-recover.c
static int equalize_all_and_refresh(void) {
    (void)wait_path_stable(LOG_PATH, 3, 150);

    if (rebuild_idx_from_log_atomic(LOG_PATH, IDX_PATH, NULL, NULL, NULL) != 0)
        return -1;

    // 若 seal 缺失/不可解析，且 log/idx 已对齐，直接本地 reseal（无需等插件）
    {
        unsigned long long l_log = count_lines_fast(LOG_PATH);
        unsigned long long l_idx = count_lines_fast(IDX_PATH);

        int s_ok = 0, sm3_ok = 0, sm3_match = 0;
        unsigned long long rec = 0;
        struct seal S = {0};
        if (parse_seal_file(SEAL_PATH, &S) == 0) {
            s_ok = 1;
            if (S.records) rec = strtoull(S.records, NULL, 10);
            char idx_hex[65] = {0};
            sm3_ok = (sm3_file_hex(IDX_PATH, idx_hex) == 0);
            sm3_match = (sm3_ok && S.index_sm3 && strcmp(idx_hex, S.index_sm3) == 0);
        }

        if (l_log == l_idx && (!s_ok || rec != l_idx || !sm3_match)) {
            if (local_reseal_from_idx_tail() == 0) return 0;
            // reseal 失败再进入“尝试通知插件”的流程
        }
    }

    // 走到这里：seal 看起来存在且匹配，或本地 reseal 失败，需要尽量通知插件刷新尾部
    const int tries = 8;  // 最多重试 ~8*200ms=1.6s，收敛更快
    for (int t=0; t<tries; t++) {
        int kr = poke_gmseal_recheck();
        if (kr == -2) {
            // 插件不可达或无权限 -> 直接本地 reseal（避免等待/噪音）
            if (local_reseal_from_idx_tail() == 0) return 0;
            return -1;
        }
        // kr == -1 -> 轻微瞬时失败，继续尝试；kr == 0 -> 已通知，等一会儿
        usleep(200 * 1000);

        // 每次都检查是否已经对齐
        unsigned long long l_log = count_lines_fast(LOG_PATH);
        unsigned long long l_idx = count_lines_fast(IDX_PATH);

        int s_ok = 0, sm3_ok = 0, sm3_match = 0;
        unsigned long long rec = 0;
        struct seal S = {0};
        if (parse_seal_file(SEAL_PATH, &S) == 0) {
            s_ok = 1;
            if (S.records) rec = strtoull(S.records, NULL, 10);
            char idx_hex[65] = {0};
            sm3_ok = (sm3_file_hex(IDX_PATH, idx_hex) == 0);
            sm3_match = (sm3_ok && S.index_sm3 && strcmp(idx_hex, S.index_sm3) == 0);
        }
        if (l_log == l_idx && s_ok && rec == l_idx && sm3_match) return 0;
    }

    // 仍未就绪：最后再尝试一次本地 reseal
    if (local_reseal_from_idx_tail() == 0) return 0;
    return -1;
}




// 恢复 seal 和 idx 文件
/* === 修改 recover_seal_idx 恢复逻辑 === */
int recover_seal_idx() {
    // printf("Checking for degraded mode...\n");
    printf(_("Checking for degraded mode...\n"));
    int need_rebuild_idx = 0;

    /* 情况1：idx 缺失 -> 必须重建 */
    if (!file_exists(IDX_PATH)) need_rebuild_idx = 1;

    /* 情况2：seal 不存在/不可解析/算法不对 -> 降级，仍然重建 idx，
       让插件在下一条审计事件到来时写回新的 seal */
    struct seal S = {0};
    int seal_ok   = file_exists(SEAL_PATH) && (parse_seal_file(SEAL_PATH, &S) == 0)
                    && safe_streq(S.hash,"sm3") && safe_streq(S.sign,"sm2");

    if (!seal_ok) need_rebuild_idx = 1;

    /* 情况3：如果 seal 有 index_sm3，且与 idx 当前值不一致 -> 重建 idx */
    if (!need_rebuild_idx && S.index_sm3) {
        char idx_hex[65];
        if (sm3_file_hex(IDX_PATH, idx_hex) != 0 || strcmp(idx_hex, S.index_sm3) != 0) {
            need_rebuild_idx = 1;
        }
    }

    if (!need_rebuild_idx) {
        // printf("No rebuild needed. Files look consistent.\n");
        printf(_("No rebuild needed. Files look consistent.\n"));
        return 0;
    }

    // ★ 确保 idx 有占位（等价你的“先手动创建空文件”）
    (void)ensure_idx_placeholder();

    /* 执行 idx 原子重建（不依赖 seal 正常与否）*/
    if (rebuild_idx_from_log_atomic(LOG_PATH, IDX_PATH, NULL, NULL, NULL) != 0) {
        // printf("Rebuild idx failed.\n");
        printf(_("Rebuild idx failed.\n"));
        return -1;
    }

    int eq = equalize_all_and_refresh();
    if (eq == 0) {
        // printf("Idx/seal/log fully synchronized.\n");
        printf(_("Idx/seal/log fully synchronized.\n"));
    } else if (eq == -2) {
        // printf("Idx rebuilt and log aligned, but seal still missing. "
        //        "Sent wake signals to gmseal; if it persists, check /var/run/audisp-gmseal.lock PID "
        //        "and plugin logs.\n");
        printf(_("Idx rebuilt and log aligned, but seal still missing. "
                 "Sent wake signals to gmseal; if it persists, check /var/run/audisp-gmseal.lock PID "
                 "and plugin logs.\n"));
    } else {
        // printf("Idx rebuilt. Notified gmseal to recheck and resync tail.\n");
        printf(_("Idx rebuilt. Notified gmseal to recheck and resync tail.\n"));
    }

    (void)poke_gmseal_recheck();
    return 0;
}


// PATCH: 更严格的降级判定
int is_degraded_mode() {
    if (!file_exists(SEAL_PATH)) return 1;
    if (!file_exists(IDX_PATH))  return 1;

    struct seal S = {0};
    if (parse_seal_file(SEAL_PATH, &S) != 0) return 1;  // seal 不可解析 → 降级
    if (!safe_streq(S.hash, "sm3") || !safe_streq(S.sign, "sm2")) return 1;

    // 一定要有 index_sm3，且与当前 idx 一致（否则也判降级）
    if (!S.index_sm3) return 1;
    char idx_hex[65] = {0};
    if (sm3_file_hex(IDX_PATH, idx_hex) != 0) return 1;
    if (strcmp(idx_hex, S.index_sm3) != 0) return 1;

    return 0;
}


// 恢复 seal 和 idx 文件
int recover_files() {
    // printf("Starting file recovery...\n");
    printf(_("Starting file recovery...\n"));

    // 检查系统是否在降级模式
    if (is_degraded_mode()) {
        // 进入降级模式，尝试恢复文件
        // printf("System is in degraded mode, attempting to restore seal and idx...\n");
        printf(_("System is in degraded mode, attempting to restore seal and idx...\n"));
        return recover_seal_idx();  // 恢复 seal 和 idx 文件
    } else {
        // printf("System is running normally, no need for recovery.\n");
        printf(_("System is running normally, no need for recovery.\n"));
        return 0;  // 不需要恢复
    }
}

int main(int argc, char *argv[]) {
    // 解析命令行参数，或进行其他初始化工作
        /* ---- parse --lang like audit-keygen.c ---- */
    const char *opt_lang = NULL;
    for (int i=1;i<argc;++i){
        if (strncmp(argv[i],"--lang=",7)==0) opt_lang = argv[i]+7;
        else if (!strcmp(argv[i],"--lang") && i+1<argc) { opt_lang = argv[i+1]; ++i; }
    }
    /* default force C/English */
    setenv("LANGUAGE","C",1);
    setlocale(LC_ALL,"C");
    if (opt_lang && *opt_lang && strcasecmp(opt_lang,"en")!=0 && strcasecmp(opt_lang,"C")!=0){
        if (!setlocale(LC_ALL,opt_lang)) {
            char buf[128]; snprintf(buf,sizeof(buf),"%s.UTF-8",opt_lang);
            if (!setlocale(LC_ALL,buf)) setlocale(LC_ALL,"");
        }
        setenv("LANGUAGE", opt_lang, 1);
    }
    i18n_init();
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    // 执行恢复操作
    if (recover_files() == 0) {
        // printf("Files successfully recovered and system is back to normal.\n");
        printf(_("Files successfully recovered and system is back to normal.\n"));
    } else {
        // printf("File recovery failed. Please check system logs.\n");
        printf(_("File recovery failed. Please check system logs.\n"));
    }

    return 0;
}
